/*********************************************************************************
Organization 					: 				Georgia Institute of Technology
  
Institute 						:				Cognitive Computing Group(CCL)
 
Authors							: 				Santiago Ontanon, Kinshuk Mishra
 												Neha Sugandh 
 												
Class							:				BasicOperatorPlan
 
Function						: 				Provides a Basic Operator Plan 
												
****************************************************************************/
package planninglanguage.plans;

import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

import mre.MRE;

import org.jdom.Element;

import planninglanguage.conditions.Condition;
import util.XMLWriter;
import base.WargusGameState;
import base.WargusUnit;
import cbr.CBR;

abstract public class BasicOperatorPlan extends Plan {
	int m_unitID = -1;
	BasicOperatorPlan m_original = null; /* contains a reference to the plan before adaptation */ 

	WargusUnit m_originalUnit = null;	/* Contains all the information about the original unit
										   associated with this operator in the trace from where
										   it was extracted.										*/ 
	
	
	public BasicOperatorPlan(Condition a_precondition,Condition a_aliveCondition,Condition a_successCondition)
	{
		super(a_precondition,a_aliveCondition,a_successCondition);
		m_firstExecutionCallCycle = -1;
		m_ExecutionStartCycle = -1;
	}
	
	
	public BasicOperatorPlan(Condition a_precondition,Condition a_aliveCondition,Condition a_successCondition, Plan a_parent)
	{
		super(a_precondition,a_aliveCondition,a_successCondition);
		m_firstExecutionCallCycle = -1;
		m_ExecutionStartCycle = -1;
		m_parent = a_parent;
	}
	
	public void getOpenGoals(LinkedList<SubGoalPlan> openGoals) {};

	public void substitutePlan(Plan find, Plan replace) {};
	
	public void updateParentHierarchy(Plan parent) {
		setParent(parent);
	}
	
	public void resetPlanStatus(WargusGameState a_currentGameState) {
		resetConditions();
		setStatus(Plan.READY);
		setFirstExecutionCallCycle(-1);
	}
	
	public void getExecutableActions(LinkedList<BasicOperatorPlan> executableActions) {
		if (getStatus() == Plan.READY) {
			executableActions.add(this);
		}
	}
	
	public void getTentativeActions(LinkedList<BasicOperatorPlan> tentativeActions) {
		 if(getStatus() == Plan.READY) {
			 tentativeActions.add(this);
		 }
	}
	
	public void getExecutingIds(LinkedList<Integer> executingUnits) {
		if(getStatus() == Plan.EXECUTING) {
	    	executingUnits.add(new Integer(getUnitID()));
	    }
	}
	
	// XXX debug printouts may need to be changed..
	public void updateStatus(WargusGameState a_currentGameState, long a_curCycle, CBR a_cbr) {
		if (statusComplete()) return;
		if (getStatus() == Plan.EXECUTING) {
			if (getSuccessCondition() == null
					|| getSuccessCondition().test(a_currentGameState)) {
				setStatus(Plan.SUCCEEDED);
				MRE.DEBUG(2, "MRE: Behavior succeeded");
//			m_trace.createSuccessEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle());						
			}
			if(getAliveCondition()!=null && 
					!getAliveCondition().test(a_currentGameState)) {
				setStatus(Plan.FAILED);
				MRE.DEBUG(2, "MRE: Behavior failed because of the alive conditions");
				
				List<Condition> l = new LinkedList<Condition>();
				getFailedConditions(getAliveCondition(),l, a_currentGameState);
				
//					m_trace.createFailureEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle(),l, Trace.FailureConditionType.ALIVECONDITION);						
			}
			long cycleDifference = a_curCycle - getExecuteStartCycle();
			if(cycleDifference > MRE.ACTION_SUCCESS_LIMIT) {
				setStatus(Plan.FAILED);
				MRE.DEBUG(2, "MRE: Basic operator is executing. Timed out!");
/*					{
					List<Condition> l = new LinkedList<Condition>();
					l.add(new TimeOutCondition(4000,TimeOutCondition.TimeOutType.end));
// 						m_trace.createFailureEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle(),l, Trace.FailureConditionType.POSTCONDITION);
				}						
*/
			} else {
				MRE.DEBUG(2, "MRE: Basic operator is executing. Waiting... " + 
						cycleDifference + "/" + MRE.ACTION_SUCCESS_LIMIT);						
			}
		}
	}
	
	public WargusUnit getOriginalUnit() {
		return m_originalUnit;
	}
	
	
	public void setOriginalUnit(WargusUnit wu) {
		m_originalUnit = wu;
		if (wu != null) 
			setUnitID(wu.getUnitID());
		else 
			setUnitID(-1);
	}
	
	
	public void setOriginal(BasicOperatorPlan original) {
		m_original = original;
	}

	public BasicOperatorPlan getOriginal() {
		return m_original;
	}
	
	public int getUnitID() {
		return m_unitID;
	}
	
	public void setUnitID(int a_unitID) {
		m_unitID = a_unitID;
	}
	
	public boolean send()
	{
		return true;
	}
	
	public String toString(int tabs) {
		String str= "";
		int i;
		
		for(i=0;i<tabs;i++) str = str + "  ";
		str = str + "BasicOperatorPlan()\n";
		str = str + conditionsToString(tabs);
		return str;
	}	
	
	public boolean sameGoal(Object other)
	{
		if (getClass() != other.getClass()) return false;

		BasicOperatorPlan o = (BasicOperatorPlan) other;
		   
		if (m_unitID!=o.m_unitID) return false;		   
		return true;
	}
	
	protected void writeBOPlanAttrsXML(XMLWriter w) {
		super.writePlanAttrsXML(w);
		w.tag("unit-id", m_unitID);
		w.tag("original-plan");			
		{
			BasicOperatorPlan.writeXML(w, m_original);
		}
		w.tag("/original-plan");
		
		w.tag("original-unit");
		{
			WargusUnit.writeXML(w, m_originalUnit);
		}
		w.tag("/original-unit");
	}
	
	protected final static void setBOPlanAttrsFromXML(BasicOperatorPlan p, Element e) {
		
		p.setOriginal(BasicOperatorPlan.loadBOPFromXML(
				e.getChild("original-plan").getChild("basicoperator-plan")));
		p.setOriginalUnit( 
			WargusUnit.loadFromXML(e.getChild("original-unit").getChild("unit")));
		// this must be after getting original unit b/c original unit will set unitID to -1 if null original unit
		// why does it set id anyways???
		p.setUnitID(Integer.parseInt(e.getChildText("unit-id")));
/*		if (p.getOriginalUnit() != null && p instanceof ResourceLocationBOPlan) {
			p.setUnitID(p.getOriginalUnit().getUnitID()); // XXX eww but what can i do?
		}
*/
	}
	
	protected static final String XML_OPEN_TAG = "basicoperator-plan";
	protected static final String XML_CLOSE_TAG = "/basicoperator-plan";
	protected final String XMLopenTag() {
		return XML_OPEN_TAG + super.XMLplanAttributes(); 
	}
	protected final String XMLcloseTag() {
		return XML_CLOSE_TAG;
	}
	abstract public void writeXML(XMLWriter w);
	
	public static void writeXML(XMLWriter w, BasicOperatorPlan p) {
		if (p == null) {
			w.tag("basicoperator-plan", "");
		} else {
			p.writeXML(w);
		}
	}

	protected static BasicOperatorPlan loadBOPFromXML(Element e) {
		assert e.getName().equals("basicoperator-plan") :
			"BasicOperatorPlan.loadFromXML: Invalid XML Element " + e.getName();
		
		BasicOperatorPlan plan = null;
		
		String planType = e.getAttributeValue("type");
		if (planType == null) return null; 
		try {
			Method[] methods =
				Class.forName("planninglanguage.plans." + planType).getDeclaredMethods();
			for (Method m : methods) {
				if (m.getName().equals("loadFromXML")) {
					plan = (BasicOperatorPlan) m.invoke(null, new Object[]{e});
					Plan.setPlanAttrsFromXML(plan, e);
					BasicOperatorPlan.setBOPlanAttrsFromXML(plan, e);
					return plan;
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			System.exit(0);
		}
		throw new Error("BasicOperatorPlan.loadBOPFromXML: unsupported plan type '" + planType + "'");
	}
}
